#ifndef __COMMON_UTILS_H__
#define __COMMON_UTILS_H__

#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/epoll.h>

#include <string>
#include <utility>
#include <random>
#include <thread>

template <class T>
class Singleton
{
public:
    static T* instance() {
        static T obj; 
        return &obj;
    }
};

template <class T>
class ThreadSingleton
{
public:
    static T* instance() {
        static thread_local T obj; 
        return &obj;
    }
};

class CommonUtils
{
public:
    inline static long now()
    {
        return time(0); 
    }

    inline static long now_ms()
    {
        struct timeval tv; 
        gettimeofday(&tv, NULL);
        return tv.tv_sec * 1000 + tv.tv_usec / 1000;
    }

	inline static long now_us()
	{
        struct timeval tv; 
        gettimeofday(&tv, NULL);
		return (long)tv.tv_sec * 1000000 + tv.tv_usec;
	}

    inline static int gettid()
    {
        return syscall(SYS_gettid); 
    }

	bool set_thread_name(pthread_t tid, const std::string& name)
	{
		if (name.length() > 15) {
			return false;
		}
		return pthread_setname_np(tid, name.c_str()) == 0;
	}

	template <class T>
	static std::string get_class_name()
	{
        std::string name = typeid(T).name();
        const char*p = name.c_str();
        while (*p != 0 && *p >= '0' && *p <= '9') {
            p++;
        }
        return p;
	}

    template <class F, class... Args>
    static auto call_without_intr(F&& f, Args&&... args)
        -> typename std::result_of<F(Args...)>::type
    {
        using R = typename std::result_of<F(Args...)>::type;
    retry: 
        R ret = f(std::forward<Args>(args)...);
        if (ret == -1 && errno == EINTR) {
            goto retry;
        }
        return ret;
    }

    static std::string format_string(const std::string& fmt)
    {
        return fmt; 
    }

    template <class... Args>
    static std::string format_string(const std::string& fmt, Args... args)
    {
        const int FORMAT_STRING_SIZE = 1024; 

        char* p = NULL;
        char  buf[FORMAT_STRING_SIZE];
        std::shared_ptr<char> ptr;

        auto size = snprintf((char*)NULL, 0, fmt.c_str(), args...) + 1;
        if (size > sizeof(buf)) {
            ptr = std::shared_ptr<char>(new char[size], [](char* p) {delete[] p;}); 
            p = ptr.get();
        } else {
            p = buf; 
        }
        snprintf(p, size, fmt.c_str(), args...);
        return p;
    }

    static std::string date_ms(long time_ms = 0)
    {
        char date[128]; 
        long sec, msec;
        if (time_ms > 0) {
            sec  = time_ms / 1000; 
            msec = time_ms % 1000;
        } else {
            struct timeval tv; 
            gettimeofday(&tv, NULL);
            sec  = tv.tv_sec;
            msec = tv.tv_usec / 1000;
        }

        struct tm tmm;
        localtime_r(&sec, &tmm);
        snprintf(
            date, 
            sizeof(date), 
            "%04d-%02d-%02d_%02d:%02d:%02d.%03ld", 
        	tmm.tm_year + 1900,
        	tmm.tm_mon + 1,
        	tmm.tm_mday,
        	tmm.tm_hour,
        	tmm.tm_min,
        	tmm.tm_sec,
        	msec
        );
        return date;
    }

    static std::string epoll_events_to_string(int events)
    {
    	std::string s = "";
    	if (events & EPOLLIN) {
    	    s += "EPOLLIN,";
    	}
    	if (events & EPOLLOUT) {
    	    s += "EPOLLOUT,";
    	}
    	if (events & EPOLLRDHUP) {
    	    s += "EPOLLRDHUP,";
    	} 
    	if (events & EPOLLPRI) {
    	    s += "EPOLLPRI,"; 
    	}     
    	if (events & EPOLLERR) {
    	    s += "EPOLLERR,";
    	} 
    	if (events & EPOLLHUP) {
    	    s += "EPOLLHUP,"; 
    	}
    	if (events & EPOLLET) {
    	    s += "EPOLLET,";  
    	}
    	if (events & EPOLLONESHOT) {
    	    s += "EPOLLONESHOT,"; 
    	}
    	if (s.length() > 0) {
    	    s = s.substr(0, s.length() - 1);
    	}
    	return s; 
    }
};

#endif

